/**
 * \file Declaration.h
 * \brief Headers for declaration-oriented Code subclasses
 * \author Wouter Caarls <w.caarls@tnw.tudelft.nl> 
 *
 * \verbatim
 * Copyright (c) 2005 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

#ifndef __DECLARATION_H_INCLUDED
#define __DECLARATION_H_INCLUDED

#include "CStr.h"
#include "Type.h"
#include "Code.h"
#include "LeftExpression.h"
#include "Value.h"

class TypeId : public Code
{
  public:
    TypeId(Type *_type, std::string _name="") : Code(_type), name(_name) {}

    TypeId *copy()
    {
      return new TypeId(type, name);
    }

    Code *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    std::string toString(int indentation=0);
    std::string toTerm();

  public:
    std::string name;
};

class TypeSpec;
class IdDecl;

class MemDecl : public Code
{
  public:    
    MemDecl(TypeSpec *_typespec, IdDecl *_iddecl) :
      typespec(_typespec), iddecl(_iddecl) {}

    MemDecl *copy();

    Code *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    std::string toString(int indentation=0);
    std::string toTerm();

  public:
    TypeSpec *typespec;
    IdDecl* iddecl;
};

class StructSpec : public Code
{
  public:
    typedef std::vector<MemDecl*>::iterator MI;

    StructSpec() : Code() {}
    StructSpec(Type *_type) : Code(_type) {}

    StructSpec *copy()
    {
      StructSpec *res = new StructSpec(type);
      
      if (!memdecls.empty())
	for (MI ii=memdecls.begin(); ii != memdecls.end(); ++ii)
	  res->push_back((*ii)->copy());
      
      return res;
    }

    Code *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    std::string toString(int indentation=0);
    std::string toTerm();

    void push_back(MemDecl* memdecl)
    {
      memdecls.push_back(memdecl);
    }

  public:
    std::vector<MemDecl*> memdecls;
};

class TypeSpec : public Code
{
  public:
    TypeSpec(Type *_type, Code *_spec=NULL) : Code(_type), spec(_spec) {}

    TypeSpec *copy()
    {
      if (spec)
	return new TypeSpec(type, spec->copy());
      else
	return new TypeSpec(type);
    }

    Code *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    std::string toString(int indentation=0);
    std::string toTerm();

  public:
    Code *spec;
};

class TypeDecode : public Code
{
  public:
    TypeDecode(Type *_type, Expression *_data) : Code(_type), data(_data) {}

    TypeDecode *copy()
    {
      return new TypeDecode(type, data);
    }

    Code *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    std::string toString(int indentation=0);
    std::string toTerm();

  public:
    Expression *data;
};

class PointerOf : public Code
{
  public:
    PointerOf(bool _isConst=false, bool _isVolatile=false, bool _isRestrict=false) :
      isConst(_isConst), isVolatile(_isVolatile), isRestrict(_isRestrict) { }

    PointerOf *copy()
    {
      return new PointerOf(isConst, isVolatile, isRestrict);
    }

    Code *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed)
    {
      return new VoidValue();
    }
    
    std::string toString(int indentation=0);
    std::string toTerm();

  public:
    bool isConst,           ///< "const" qualifier
         isVolatile,        ///< "volatile" qualifier
         isRestrict;        ///< "restrict" qualifier
};

class TypeName : public Code
{
  public:
    typedef std::vector<PointerOf*>::iterator DI;
    typedef std::vector<IntegerValue*>::iterator BI;

    TypeName(TypeSpec *_typespec) : typespec(_typespec) {}

    TypeName *copy()
    {
      TypeName *res = new TypeName(typespec->copy());
      
      if (!derefs.empty())
        for (DI ii=derefs.begin(); ii != derefs.end(); ++ii)
          res->push_back((*ii)->copy());
    
      if (!bounds.empty())
        for (BI ii=bounds.begin(); ii != bounds.end(); ++ii)
          res->push_back((*ii)->copy());
    
      return res;
    }

    ~TypeName()
    {
      delete typespec;
      
      if (!derefs.empty())
        for (DI ii=derefs.begin(); ii != derefs.end(); ++ii)
          delete *ii;
      if (!bounds.empty())
        for (BI ii=bounds.begin(); ii != bounds.end(); ++ii)
          delete *ii;
    }

    Code *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    std::string toString(int indentation=0);
    std::string toTerm();
    
    void push_back(PointerOf *d);
    void push_back(IntegerValue *b);

  public:
    TypeSpec *typespec;
    std::vector<PointerOf*> derefs;
    std::vector<IntegerValue*> bounds;
};

class IdDecl;

class ParamDecl : public Code
{
  public:
    ParamDecl() : Code(), typespec(NULL), iddecl(NULL) {}
    ParamDecl(TypeSpec *_typespec) : Code(), typespec(_typespec), iddecl(NULL) {}
    ParamDecl(TypeSpec *_typespec, IdDecl *_iddecl) : Code(), typespec(_typespec), iddecl(_iddecl) {}

    ~ParamDecl();
    ParamDecl *copy();

    Code *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    std::string toString(int indentation=0);
    std::string toTerm();

  public:
    TypeSpec *typespec;
    IdDecl *iddecl;
};


class IdDecl : public Code
{
  public:
    typedef std::vector<ParamDecl*>::iterator PI;
    typedef std::vector<IntegerValue*>::iterator BI;
    typedef std::vector<IntegerValue*>::reverse_iterator RBI;
    typedef std::vector<PointerOf*>::iterator DI;

    IdDecl(Code *_id=NULL, Expression *_init=NULL) : Code(), id(_id),
      varargparam(NULL), init(_init) {}

    ~IdDecl()
    {
      delete id;
      delete varargparam;

      if (!derefs.empty())
	for (DI ii=derefs.begin(); ii != derefs.end(); ++ii)
	  delete *ii;
   
      if (!params.empty())      
	for (PI ii=params.begin(); ii != params.end(); ++ii)
	  delete *ii;

      if (!bounds.empty())      
	for (BI ii=bounds.begin(); ii != bounds.end(); ++ii)
	  delete *ii;
    }
    
    IdDecl *copy()
      {
	IdDecl *res = new IdDecl(id->copy());

	if (varargparam)
	  res->varargparam = varargparam->copy();

	if (init)
	  res->init = init->copy();

        if (!derefs.empty())
	  for (DI ii=derefs.begin(); ii != derefs.end(); ++ii)
	    res->push_back((*ii)->copy());

        if (!params.empty())
  	  for (PI ii=params.begin(); ii != params.end(); ++ii)
	    res->push_back((*ii)->copy());

        if (!bounds.empty())
  	  for (BI ii=bounds.begin(); ii != bounds.end(); ++ii)
	    res->push_back((*ii)->copy());

	res->type = type;

	return res;
      }

    Code *replace(Code *src, Code *rplc,
                              SymbolTable *symbols, Compound *out)
      {
	IdDecl *res = new IdDecl(dynamic_cast<Code*>(id->replace(src,rplc,symbols,out)));

	if (varargparam)
	  res->varargparam = varargparam->copy();

	if (init)
	  res->init = dynamic_cast<Expression*>(init->replace(src,rplc,symbols,out));

        if (!derefs.empty())
	  for (DI ii=derefs.begin(); ii != derefs.end(); ++ii)
	    res->push_back((*ii)->copy());

        if (!params.empty())
  	  for (PI ii=params.begin(); ii != params.end(); ++ii)
	    res->push_back((*ii)->copy());

        if (!bounds.empty())
  	  for (BI ii=bounds.begin(); ii != bounds.end(); ++ii)
	    res->push_back((*ii)->copy());

	res->type = type;

	return res;
      }

    Code *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    std::string toString(int indentation=0);
    std::string toTerm();
    void push_back(ParamDecl *p);
    void push_back(IntegerValue *b);
    void push_back(PointerOf *d);

  public:
    Code *id;
    std::vector<PointerOf*> derefs;
    std::vector<ParamDecl*> params;
    Id *varargparam;
    std::vector<IntegerValue*> bounds;
    Expression *init;
};

class Declaration2 : public Code
{
  public:
    typedef std::vector<IdDecl*>::iterator II;

    Declaration2() : Code(&BasicVoid) {}
    Declaration2(TypeSpec *_typespec, bool _tentative=false,
                 bool _isStatic=false, bool _isAuto=false, bool _isPromote=false,
		 bool _isExtern=false) :
      Code(&BasicVoid, false, _tentative), typespec(_typespec),
      isStatic(_isStatic), isAuto(_isAuto), isPromote(_isPromote), isExtern(_isExtern) {}

    ~Declaration2()
    {
      delete typespec;

      if (iddecls.empty())
	return;

      for (II ii=iddecls.begin(); ii != iddecls.end(); ++ii)
	delete *ii;
    }

    Declaration2 *copy()
      {
	Declaration2 *res = new Declaration2(typespec->copy(), tentative, isStatic, isAuto, isPromote, isExtern);

	for (II ii=iddecls.begin(); ii != iddecls.end(); ++ii)
	  res->push_back((*ii)->copy());

	return res;
      }

    Code *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualize(SymbolTable *symbols);
    void clear(void);
    
    Code *replace(Code *src, Code *rplc,
                              SymbolTable *symbols, Compound *out)
    {
      Declaration2 *res = new Declaration2(typespec->copy(), tentative, isStatic, isAuto, isPromote, isExtern);

      for (II ii=iddecls.begin(); ii != iddecls.end(); ++ii)
	res->push_back(dynamic_cast<IdDecl*>((*ii)->replace(src, rplc, symbols, out)));
      
      return res;
    }                                  

    std::string toString(int indentation=0);
    std::string toTerm();
    void push_back(IdDecl *p);

  public:
    TypeSpec *typespec;
    std::vector<IdDecl*> iddecls;
    bool isStatic, isAuto, isPromote, isExtern;
};

class FunDef : public Code
{
  public:
    FunDef() : Code() {}
    FunDef(TypeSpec *_typespec, IdDecl *_iddecl, Code *_data) : Code(), typespec(_typespec),
      iddecl(_iddecl), data(_data) {}
    FunDef(TypeSpec *_typespec, IdDecl *_iddecl, Code *_data, bool _tentative,
           bool _isStatic=false, bool _isAuto=false, bool _isPromote=false,
           bool _isExtern=false) :
      Code(&BasicVoid, false, _tentative), typespec(_typespec), iddecl(_iddecl),
      data(_data), isStatic(_isStatic), isAuto(_isAuto), isPromote(_isPromote),
      isExtern(_isExtern) {}

    ~FunDef()
    {
      delete typespec;
      delete iddecl;
    }

    virtual FunDef *copy()
      {
	if (data)
	  return new FunDef(typespec->copy(), iddecl->copy(), data->copy(), tentative, isStatic, isAuto, isPromote, isExtern);
	else
	  return new FunDef(typespec->copy(), iddecl->copy(), NULL, tentative, isStatic, isAuto, isPromote, isExtern);
      }

    virtual Code *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    virtual std::string toString(int indentation=0);
    virtual std::string toTerm();

  public:
    TypeSpec *typespec;
    IdDecl *iddecl;
    Code *data;
    bool isStatic, isAuto, isPromote, isExtern;
};

class StrategyDef : public FunDef
{
  public:
    StrategyDef() : FunDef() {}
    StrategyDef(IdDecl *_iddecl, Code *_data, bool _tentative=false) : FunDef(new TypeSpec(&BasicCode), _iddecl, _data, _tentative) {}

    StrategyDef *copy()
      {
	if (data)
	  return new StrategyDef(iddecl->copy(), data->copy(), tentative);
	else
	  return new StrategyDef(iddecl->copy(), NULL, tentative);
      }
    Code *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    std::string toString(int indentation=0);
    std::string toTerm();
};

class TypeDef : public Code
{
  public:
    TypeDef() : Code() {}
    TypeDef(TypeSpec *_typespec, IdDecl *_iddecl, bool _tentative=false) :
      Code(&BasicVoid, false, _tentative), typespec(_typespec), iddecl(_iddecl) {}

    ~TypeDef()
    {
      delete typespec;
      delete iddecl;
    }

    TypeDef *copy()
    {
      return new TypeDef(typespec->copy(), iddecl->copy(), tentative);
    }

    Code *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualize(SymbolTable *symbols);

    std::string toString(int indentation=0);
    std::string toTerm();

  public:
    TypeSpec *typespec;
    IdDecl *iddecl;
};



#endif /* __DECLARATION_H_INCLUDED */
